import pygame, sys, copy
from pygame.locals import *
from tower import *
from game_elements import *
from solution import *

TOWER_R = 6 # tower radius
TOWER_CLASSES = { 'arrow': ArrowTower,
                  'ice': IceTower,
                  'stone': StoneTower }

UI_POS = (600, 0)
BTN_WID = 40
BTNS = { 'arrow': { 'pos' : (UI_POS[0] + 40, UI_POS[1] + 80),  'img': None },
         'ice'  : { 'pos' : (UI_POS[0] + 40, UI_POS[1] + 160), 'img': None },
         'stone': { 'pos' : (UI_POS[0] + 40, UI_POS[1] + 240), 'img': None },
         'fire' : { 'pos' : (UI_POS[0] + 40, UI_POS[1] + 320), 'img': None } }

BASE_PARAMS = lambda: [10, 100, 2, 0]

images = {}
mouse_down = False
holding = {'tower': None}
game_map = None

tower_gen = {
    'arrow': lambda pos: ArrowTower(100, 25, 5, 1, pos),
    'ice'  : lambda pos: IceTower(60, 40, 16, 1, pos, {'rate':0.5,'time':24}),
    'stone': lambda pos: StoneTower(80, 52, 25, 1, pos, 0.4, 80, game_map),
    'fire' : lambda pos: FireTower(60, 80, 40, 1, pos, 0.4, 80, game_map)
}
"""
def main():
    path = [(0,200), (20,200), (20,240), (80,240)]
    game_map = Map(600, 4000, path)

    t1 = ArrowTower(10, 5, 2, 1, pos=(21,200))
    t2 = IceTower(10, 2, 3, 1, (21,201), {'rate':0.5, 'time':5})
    t3 = StoneTower(8, 4, 2, 1, (19,200), 0.2, 20, game_map)
    towers = [t1,t2, t3]

    g = {'pos': path[1], 'id': 1}
    m = Monster(max_hp=200, speed=3, defence=0, position=path[0], goal=g)

    evaluate(towers, [m, m, m, m], game_map)
"""

def evaluate(towers, monsters, game_map, ui = None, log = None):
    def update():
        for t in towers:
            if t.active():
                ms = game_map.targets(t.position, t.gunshot)
                if len(ms) != 0 : t.attack(ms[0])
            t.reflesh()

        monsters = game_map.monsters()
        for monster in monsters:
            d = game_map.move_monster(monster)
            if not (log is None or d is True):
                log['all_d'] += d
                log['max_d'] = max(log['max_d'], d) 
            monster.update_state()
        if not monsters: return True

    F = 10
    i = 0
    while True:
        if ui: ui(towers, game_map)

        if monsters:
            if (i % F) == 0: game_map.add_monster(monsters.pop())
            i += 1

        if(update() and not monsters):
            break


def game():
    # set up pygame
    pygame.init()
    init_tower_images()
    init_buttons_images()

    FPS = 30
    fps_clock = pygame.time.Clock()    

    # set up the window
    win_surf = pygame.display.set_mode((800, 400), 0, 32)
    pygame.display.set_caption('TD 0.0')

    path = [(60,0), (60,100), (180,100), (180,240), (60,240), (60,360), (420,360), (420,40), (540,40), (540,360)]
    global game_map
    game_map = Map(600, 400, path)

    towers = []
    the = {'towers': towers}

    g = {'pos': path[1], 'id': 1}
    basic_monser = Monster(max_hp=100, speed=3, defence=0, position=path[0], goal=g)
    parameters = [1, 10, 1, 0]

    def clear_towers():
        del the['towers'][:]

    def evaluate_show_fun(towers, g_map):
        draw_elements(win_surf, towers, g_map)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        pygame.display.update()
        fps_clock.tick(FPS)

    def play_trun():
        def click_btn(event):
                for b in BTNS:                
                    if in_button(event.pos, b):
                        holding['tower'] = b
                        return True
                return False

        def put_tower(event):
            if event.pos[0] >= UI_POS[0]:
                holding['tower'] = None
            else :
                add_tower(towers, holding['tower'], event.pos)
            

        def mouse_click(event):
            #print "click ", event.pos
            if click_btn(event): return
            elif holding['tower']: put_tower(event)
            
        end = False
        while not end:
            draw_elements(win_surf, towers, game_map, no_monster = True)
            for event in pygame.event.get():
                if event.type == QUIT:
                    pygame.quit()
                    sys.exit()                
                if event.type == KEYDOWN:
                    if event.key == ord('e'):
                        end = True
                        break
                    elif event.key == ord('w'):
                        end = True

                        de = DE(game_map, towers, parameters, path)
                        new_parameter = de.get_solution(10, 0.5, 0.5).numbers
                        
                        for i in xrange(len(parameters)):                         
                            parameters[i] += new_parameter[i]
                        
                        break
                    elif event.key == ord('c'):
                        clear_towers()
                # mouse control
                if event.type == MOUSEBUTTONDOWN:
                    mouse_down = True
                if event.type == MOUSEBUTTONUP:
                    mouse_down = False
                    mouse_click(event)

            pygame.display.update()
            fps_clock.tick(FPS)


    # run the game loop
    while True:
        parameters = BASE_PARAMS()
        play_trun()  
        amount = int(parameters[0])
        max_hp = parameters[1]
        speed = parameters[2]
        armor = parameters[3]
        #print "amount:" ,amount,  "  HP: ", max_hp, " speed: ", speed, " armor: ", armor
        special_monser = Monster(max_hp, speed, armor, position=path[0], goal=g)
        monsters = [copy.deepcopy(special_monser) for i in range(amount)]
        evaluate(towers, monsters, game_map, evaluate_show_fun)
        #monsters = [copy.deepcopy(basic_monser) for i in range(10)]
        #evaluate(towers, monsters, game_map, evaluate_show_fun)


def add_tower(towers, tower_type, pos):    
    towers.append(tower_gen[tower_type](pos))

def draw_monsters(game_map, win_surf):
    R = 12
    MONSTER_SIZE = (R * 2, R * 2)

    def draw_hp_bar(monster): 
        RED = (255, 0, 0)
        WHITE = (255, 255, 255)
        w = 4
        x = 0
        y = w / 2        
        start = (x, y)
        w_end = (x + 2 * R, y)
        r_end = (x + (2 * R * m.hp) / m.MAX_HP, y)

        t_surf = pygame.Surface((MONSTER_SIZE[0], w))
        pygame.draw.line(t_surf, WHITE, start, w_end, w)
        pygame.draw.line(t_surf, RED, start, r_end, w)
        t_surf.set_alpha(180)
        p = (monster.position[0], monster.position[1] + MONSTER_SIZE[1] - 2)
        win_surf.blit(t_surf, revise_pos(p, R))

    monster_img = pygame.image.load('images/monster.png')
    monster_img = pygame.transform.scale(monster_img, MONSTER_SIZE)
    monster_f_img = pygame.image.load('images/monster_frozen.png')
    monster_f_img = pygame.transform.scale(monster_f_img, MONSTER_SIZE)

    monsters = game_map.monsters()
    for m in monsters:
        if m.frozen():
            win_surf.blit(monster_f_img , revise_pos(m.position, R))
        else:
            win_surf.blit(monster_img, revise_pos(m.position, R))
    for m in monsters:
        draw_hp_bar(m)
        #print "P = ", m.position,", HP = ", m.hp, ", G = ", m.goal, "D = ", m.direction

def draw_road(game_map, win_surf):
    C = (100,100,100)
    road = game_map.road()
    for i in range(len(road) - 1):
        pygame.draw.line(win_surf, C, road[i], road[i+1], 4)

def draw_towers(towers, win_surf):
    R = TOWER_R

    for t in towers:
        draw_tower_range(t, win_surf)

    for t in towers:        
        draw_tower(t, win_surf)

def draw_tower(t, win_surf):
    R = TOWER_R
    if isinstance(t, ArrowTower):
        win_surf.blit(images['arrow'], revise_pos(t.position, R))
    elif isinstance(t, IceTower):
        win_surf.blit(images['ice'], revise_pos(t.position, R))
    elif isinstance(t, FireTower):
        win_surf.blit(images['fire'], revise_pos(t.position, R))
    elif isinstance(t, StoneTower):
        win_surf.blit(images['stone'], revise_pos(t.position, R))


 
def draw_buttons(win_surf):
    for btn in BTNS:
        win_surf.blit(BTNS[btn]['img'], revise_pos(BTNS[btn]['pos'], BTN_WID / 2))

def draw_holding(win_surf):
    if holding['tower'] is None : return
    pos = pygame.mouse.get_pos()
    t = tower_gen[ holding['tower'] ](pos)
    draw_tower(t, win_surf)
    draw_tower_range(t, win_surf, color=(160,160,255))


def draw_elements(win_surf, towers, g_map, no_monster = False):
    win_surf.fill((0, 0, 0))
    draw_road(g_map, win_surf)
    draw_towers(towers, win_surf)
    draw_buttons(win_surf)
    if not no_monster: draw_monsters(g_map, win_surf)
    else: draw_holding(win_surf)


def revise_pos(pos, r):
    return (pos[0]-r, pos[1]-r)

def draw_tower_range(t, win_surf, color = (255,100,100), alpha = 60):
    def draw_alpha_circle(surface, color, pos, r, alpha):
        T = (255, 255, 255)
        t_surf = pygame.Surface((r*2, r*2))
        t_surf.fill(T)
        t_surf.set_colorkey(T)
        pygame.draw.circle(t_surf, color, (r,r), r)
        t_surf.set_alpha(alpha)

        surface.blit(t_surf, revise_pos(pos, r))
    draw_alpha_circle(win_surf, color, t.position, t.gunshot, alpha)

def in_button(pos, b):
    btn_pos = BTNS[b]['pos']
    return max(abs(pos[0] - btn_pos[0]), abs(pos[1] - btn_pos[1])) * 2 <= BTN_WID

def init_tower_images():
    R = TOWER_R
    TOWER_SIZE = (R * 2, R * 2)
    global images    

    arrow_tower_img = pygame.image.load('images/arrow_tower.png')
    arrow_tower_img = pygame.transform.scale(arrow_tower_img, TOWER_SIZE)
    ice_tower_img = pygame.image.load('images/ice_tower.png')
    ice_tower_img = pygame.transform.scale(ice_tower_img, TOWER_SIZE)
    stone_tower_img = pygame.image.load('images/stone_tower.png')
    stone_tower_img = pygame.transform.scale(stone_tower_img, TOWER_SIZE)
    fire_tower_img = pygame.image.load('images/fire_tower.png')
    fire_tower_img = pygame.transform.scale(fire_tower_img, TOWER_SIZE)

    images['arrow'] = arrow_tower_img
    images['ice']   = ice_tower_img
    images['stone'] = stone_tower_img
    images['fire']  = fire_tower_img 

def init_buttons_images():
    BUTTON_SIZE = (BTN_WID, BTN_WID)

    arrow_img = pygame.image.load('images/arrow_tower.png')
    BTNS['arrow']['img'] = pygame.transform.scale(arrow_img, BUTTON_SIZE)
    ice_img = pygame.image.load('images/ice_tower.png')
    BTNS['ice']['img'] = pygame.transform.scale(ice_img, BUTTON_SIZE)
    stone_img = pygame.image.load('images/stone_tower.png')
    BTNS['stone']['img'] = pygame.transform.scale(stone_img, BUTTON_SIZE)
    fire_img = pygame.image.load('images/fire_tower.png')
    BTNS['fire']['img'] = pygame.transform.scale(fire_img, BUTTON_SIZE)


if __name__ == '__main__':
    game()
